/*
 * License GNU LGPL
 * Copyright (C) 2012 Amrullah <amrullah@panemu.com>.
 */
package com.abc.cheque.ui.table;



import java.util.ArrayList;
import java.util.List;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Control;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.Callback;
import javafx.util.StringConverter;

import com.abc.cheque.common.TableCriteria.Operator;


/**
 *
 * @author amrullah
 */
public class CheckBoxColumn<S> extends BaseColumn<S, Boolean> {

    private CheckBox searchInputControl;
    private SearchMenuItemBase<Boolean> searchMenuItem;
    private String trueLabel = "True";
    private String falseLabel = "False";
    private String nullLabel = "";

    public CheckBoxColumn() {
        this("");
    }

    public CheckBoxColumn(String propertyName) {
        this(propertyName, 100);
    }

    public CheckBoxColumn(String propertyName, double prefWidth) {
        super(propertyName, prefWidth);
        setRenderer();
    }

    private void setRenderer() {


        Callback<TableColumn<S, Boolean>, TableCell<S, Boolean>> cb2 = new Callback<TableColumn<S, Boolean>, TableCell<S, Boolean>>() {
            @Override
            public TableCell<S, Boolean> call(TableColumn<S, Boolean> list) {
                return new CheckBoxTableCell<>();

            }
        };

        setCellFactory(cb2);
    }

    @Override
    public MenuItem getSearchMenuItem() {
        if (searchMenuItem == null) {
            searchInputControl = new CheckBox();
            searchInputControl.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                    searchInputControl.setText(conf.toString(searchInputControl.isSelected()));
                }
            });

            searchMenuItem = new SearchMenuItemBase<Boolean>(this) {
                @Override
                protected Node getInputControl() {
                    return searchInputControl;
                }

                @Override
                protected List<Operator> getOperators() {
                    List<Operator> lst = new ArrayList<>();
                    lst.add(Operator.eq);
                    lst.add(Operator.is_null);
                    lst.add(Operator.is_not_null);
                    return lst;
                }

                @Override
                protected Boolean getValue() {
                    return searchInputControl.isSelected();
                }
            };
        }
        if (getDefaultSearchValue() != null) {
            searchInputControl.setText(conf.toString(getDefaultSearchValue()));
            searchInputControl.setSelected(getDefaultSearchValue());
            if (searchMenuItem.getSelectedOperator() == Operator.is_null) {
                searchMenuItem.setSelectedOperator(Operator.eq);
            }
        } else {
            searchMenuItem.setSelectedOperator(Operator.is_null);
        }
        return searchMenuItem;
    }

    private BooleanProperty allowNull;
    
    /**
     * 
     * @return 
     * @deprecated use {@link #requiredProperty()}
     */
    public BooleanProperty allowNullProperty() {
        if (allowNull == null) {
            allowNull = new SimpleBooleanProperty();
            allowNull.bind(requiredProperty().not());
        }
        return allowNull;
    }
    
    /**
     * 
     * @return 
     * @deprecated use {@link #isRequired()}
     */
    public boolean isAllowNull() {
        return !requiredProperty().get();
    }

    /**
     * If null is allowed, the checkbox will support 3 states: true, false,
     * null.
     *
     * @param allowNull
     * @deprecated use {@link #setRequired(boolean)}
     */
    public void setAllowNull(boolean allowNull) {
        this.requiredProperty().set(!allowNull);
    }
    private StringConverter<Boolean> conf = new StringConverter<Boolean>() {
        @Override
        public String toString(Boolean value) {
            if (value == null) {
                return nullLabel;
            }
            if (value) {
                return trueLabel;
            } else {
                return falseLabel;
            }
        }

        @Override
        public Boolean fromString(String string) {
            if (string.equals(nullLabel)) {
                return null;
            } else if (string.equals(trueLabel)) {
                return true;
            } else if (string.equals(falseLabel)){
                return false;
            } else {
                return null;
            }
        }
    };

    @Override
    protected Boolean convertFromString_Impl(String stringValue) {
        return conf.fromString(stringValue);
    }

    @Override
    public String convertToString(Boolean value) {
        return conf.toString(value);
    }
    
    public void setTrueLabel(String trueLabel) {
        this.trueLabel = trueLabel;
    }

    public void setFalseLabel(String falseLabel) {
        this.falseLabel = falseLabel;
    }

    public void setNullLabel(String nullLabel) {
        this.nullLabel = nullLabel;
    }

    public void setLabel(String trueLabel, String falseLabel, String nullLabel) {
        this.trueLabel = trueLabel;
        this.falseLabel = falseLabel;
        this.nullLabel = nullLabel;
    }
    private Boolean[] valueOrder = new Boolean[]{Boolean.TRUE, Boolean.FALSE, null};


    private class CheckBoxTableCell<S> extends BaseCell<S, Boolean> {

        private CheckBox checkbox;

        public CheckBoxTableCell() {
            super();
            this.setAlignment(CheckBoxColumn.this.getAlignment());
        }
        
        @Override
        protected void setValueToEditor(Boolean value) {
            if (value == null && !isRequired()) {
                checkbox.setIndeterminate(true);
            } else {
                if (value == null) {
                    value = false;
                }
                checkbox.setIndeterminate(false);
                checkbox.setSelected(value);
            }
            checkbox.setText(conf.toString(value));
        }

        @Override
        protected Boolean getValueFromEditor() {
            if (!isRequired() && checkbox.isIndeterminate()) {
                return null;
            } else {
                return checkbox.isSelected();
            }
        }

        @Override
        protected Control getEditor() {
            if (checkbox == null) {
                checkbox = new CheckBox();
                checkbox.setAllowIndeterminate(!isRequired());
                final ChangeListener<Boolean> changeListener = new ChangeListener<Boolean>() {
                    @Override
                    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                        if (checkbox.isIndeterminate()) {
                            checkbox.setText(conf.toString(null));
                        } else {
                            checkbox.setText(conf.toString(checkbox.isSelected()));
                        }
                    }
                };

                checkbox.indeterminateProperty().addListener(changeListener);
                checkbox.selectedProperty().addListener(changeListener);
                /**
                 * Disable traversing focus using LEFT and RIGHT.
                 */
                checkbox.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
                    @Override
                    public void handle(KeyEvent event) {
                        if ((event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT)
                                && isEditing()) {
                            event.consume();
                        } else if (event.getCode() == KeyCode.DOWN) {
                            if (checkbox.isAllowIndeterminate()) {
                                if (checkbox.isIndeterminate()) {
                                    checkbox.setIndeterminate(false);
                                    checkbox.setSelected(true);
                                } else if (checkbox.isSelected()) {
                                    checkbox.setIndeterminate(false);
                                    checkbox.setSelected(false);
                                } else {
                                    checkbox.setIndeterminate(true);
                                }
                            } else {
                                checkbox.setSelected(!checkbox.isSelected());
                            }
                            event.consume();
                        } else if (event.getCode() == KeyCode.UP) {
                            if (checkbox.isAllowIndeterminate()) {
                                if (checkbox.isIndeterminate()) {
                                    checkbox.setIndeterminate(false);
                                    checkbox.setSelected(false);
                                } else if (checkbox.isSelected()) {
                                    checkbox.setIndeterminate(true);
                                } else {
                                    checkbox.setIndeterminate(false);
                                    checkbox.setSelected(true);
                                }
                            } else {
                                checkbox.setSelected(!checkbox.isSelected());
                            }
                            event.consume();
                        }
                    }
                });
            }
            return checkbox;
        }

        @Override
        protected String getString(Boolean value) {
            return conf.toString(value);
        }
    }
    
}
